package com.adjaisd.scarecrow.common.utils;

import com.adjaisd.scarecrow.vo.GeoCoordinate;

import java.util.List;
import java.util.Random;

import static java.lang.Math.PI;

/**
 * Created by fc.w on 2017/12/8.
 */
public class GeoUtils {

    private static final double EARTH_RADIUS = 6378.137;

    // 弧度
    private static double rad(double d) {
        return d * PI / 180.0;
    }

    // 角度
    static double deg(double rd) {
        return (rd * 180 / Math.PI);
    }

    static double normalizeLongitude(double lon) {
        double n = PI;
        if (lon > n) lon = lon - 2 * n;
        else if (lon < -n) lon = lon + 2 * n;
        return lon;
    }

    static double padZeroRight(double s) {
        double sigDigits = 8;
        s = Math.round(s * Math.pow(10, sigDigits)) / Math.pow(10, sigDigits);
        return s;
    }

    public static GeoCoordinate getRandomLocation(GeoCoordinate center, double distance) {
        if (distance <= 0) distance = 50;
        double lat, lon, brg;
        double dist = 0;
        double rad360 = 2 * PI;
        distance = distance / 1000;
        GeoCoordinate location = new GeoCoordinate();
        double maxdist = distance;
        maxdist = maxdist / EARTH_RADIUS;
        double startlat = rad(center.getLatitude());
        double startlon = rad(center.getLongitude());
        double cosdif = Math.cos(maxdist) - 1;
        double sinstartlat = Math.sin(startlat);
        double cosstartlat = Math.cos(startlat);
        dist = Math.acos((new Random().nextDouble() * cosdif + 1));
        brg = rad360 * new Random().nextDouble();
        lat = Math.asin(sinstartlat * Math.cos(dist) + cosstartlat * Math.sin(dist) * Math.cos(brg));
        lon = deg(normalizeLongitude(startlon * 1 + Math.atan2(Math.sin(brg) * Math.sin(dist) * cosstartlat, Math.cos(dist) - sinstartlat * Math.sin(lat))));
        lat = deg(lat);
        location.setLatitude(padZeroRight(lat));
        location.setLongitude(padZeroRight(lon));
        return location;
    }

    public static double getDistance(GeoCoordinate geo1, GeoCoordinate geo2) {
        return getDistance(geo1.getLatitude(), geo1.getLongitude(),
                geo2.getLatitude(), geo2.getLongitude());
    }

    /**
     * 通过经纬度获取距离(单位：米)
     *
     * @param lat1
     * @param lng1
     * @param lat2
     * @param lng2
     * @return
     */
    public static double getDistance(double lat1, double lng1, double lat2, double lng2) {
        double radLat1 = rad(lat1);
        double radLat2 = rad(lat2);
        double a = radLat1 - radLat2;
        double b = rad(lng1) - rad(lng2);
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2)
                + Math.cos(radLat1) * Math.cos(radLat2)
                * Math.pow(Math.sin(b / 2), 2)));
        s = s * EARTH_RADIUS;
        s = Math.round(s * 10000d) / 10000d;
        s = s * 1000;
        return s;
    }

    /**
     * 根据输入的地点坐标计算中心点
     *
     * @param geoCoordinateList
     * @return
     */
    public static GeoCoordinate getCenterPoint(List<GeoCoordinate> geoCoordinateList) {
        int total = geoCoordinateList.size();
        double X = 0, Y = 0, Z = 0;
        for (GeoCoordinate g : geoCoordinateList) {
            double lat, lon, x, y, z;
            lat = g.getLatitude() * PI / 180;
            lon = g.getLongitude() * PI / 180;
            x = Math.cos(lat) * Math.cos(lon);
            y = Math.cos(lat) * Math.sin(lon);
            z = Math.sin(lat);
            X += x;
            Y += y;
            Z += z;
        }

        X = X / total;
        Y = Y / total;
        Z = Z / total;
        double Lon = Math.atan2(Y, X);
        double Hyp = Math.sqrt(X * X + Y * Y);
        double Lat = Math.atan2(Z, Hyp);
        return new GeoCoordinate(Lat * 180 / PI, Lon * 180 / PI);
    }

    /**
     * 根据输入的地点坐标计算中心点（适用于400km以下的场合）
     *
     * @param geoCoordinateList
     * @return
     */
    public static GeoCoordinate getCenterPoint400(List<GeoCoordinate> geoCoordinateList) {
        // 以下为简化方法（400km以内）
        int total = geoCoordinateList.size();
        double lat = 0, lon = 0;
        for (GeoCoordinate g : geoCoordinateList) {
            lat += g.getLatitude() * PI / 180;
            lon += g.getLongitude() * PI / 180;
        }
        lat /= total;
        lon /= total;
        return new GeoCoordinate(lat * 180 / PI, lon * 180 / PI);
    }

}

